/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fi.ahola.tietorekisteri;

import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.*;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

/**
 * Luokka hoitaa tiedostojen salaukseen liittyviä toimintoja.
 *
 * Salauksena toimii salasanapohjainen salaus MD5:n ja DES:n avulla. Koodi
 * varastettu röyhkeästi sivulta:
 * {@link http://cs.saddleback.edu/rwatkins/CS4B/Crypto/} ja muokattu omiin
 * tarkoituksiin sopivaksi.
 *
 * @author f0899
 */
public class Salaaja {

    private String tiedosto;
    private String purettuTiedosto;
    private String salasana;
    private FileInputStream in;
    private FileOutputStream out;

    /**
     * Luokan konstruktori.
     */
    public Salaaja() {
    }

    /**
     * Asettaa kohdetiedoston sijainnin ja salauksessa käytetyn salasanan.
     *
     * @param tied Kohdetiedoston sijainti
     * @param sala Salauksen salasana
     */
    public void asetaKohde(String tied, String sala) {
        this.tiedosto = tied;
        this.salasana = sala;
    }

    /**
     * Purkaa tiedoston salauksen. Palauttaa Boolean-arvon true jos onnistui ja
     * Boolean-arvon false jos epäonnistui.
     *
     * @return Salauksen purun onnistumisen boolean
     * @throws Exception Virhe salauksen purussa
     * @see #asetaKohde(java.lang.String, java.lang.String)
     */
    public boolean puraTiedosto() throws Exception {

        // Purettava tiedosto
        purettuTiedosto = tiedosto;
        String password = salasana;
        in = new FileInputStream(tiedosto + ".des");
        out = new FileOutputStream(purettuTiedosto + ".dat");

        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        SecretKeyFactory keyFactory = null;
        keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey passwordKey = null;
        passwordKey = keyFactory.generateSecret(keySpec);

        // Read in the previouly stored salt and set the iteration count.

        byte[] salt = new byte[8];
        in.read(salt);
        int iterations = 100;
        PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, iterations);

        // Create the cipher and initialize it for decryption.

        Cipher cipher = null;
        cipher = Cipher.getInstance("PBEWithMD5AndDES");
        cipher.init(Cipher.DECRYPT_MODE, passwordKey, parameterSpec);

        byte[] input = new byte[64];
        int bytesRead;

        while ((bytesRead = in.read(input)) != -1) {
            byte[] output = cipher.update(input, 0, bytesRead);
            if (output != null) {
                out.write(output);
            }
        }

        byte[] output = null;
        try {
            output = cipher.doFinal();
        } catch (BadPaddingException ex) {
            //Palauttaa falsen mikäli oli väärä salasana
            return false;
        }
        if (output != null) {
            out.write(output);
        }
        in.close();
        out.flush();
        out.close();
        return true;
    }

    /**
     * Luo salatun tiedoston. Lataa .dat-päätteisen kohdetiedoston ja salaa sen
     * sisällön .des-päätteiseen kohdetiedostoon.
     *
     * @throws Exception Virhe tiedoston käsittelyssä tai salauksessa
     * @see #asetaKohde(java.lang.String, java.lang.String)
     */
    public void salaaTiedosto() throws Exception {

        in = new FileInputStream(tiedosto + ".dat");
        out = new FileOutputStream(tiedosto + ".des");

        //Käytetään PBEKeySpeciä salasanan valmistukseen
        PBEKeySpec keySpec = new PBEKeySpec(salasana.toCharArray());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDes");
        SecretKey passwordKey = keyFactory.generateSecret(keySpec);

        //Luodaan 64bittiä randomnumeroita ja hashataan sadasti
        byte[] salt = new byte[8];
        Random rnd = new Random();
        rnd.nextBytes(salt);
        int iterations = 100;

        PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, iterations);

        //Luodaan kääntökoneisto
        Cipher cipher = Cipher.getInstance("PBEWithMD5AndDes");
        cipher.init(Cipher.ENCRYPT_MODE, passwordKey, parameterSpec);

        //Kirjoitetaan salt tiedostoon
        out.write(salt);

        //Ei mitään hajua mitä tapahtuu
        byte[] input = new byte[64];
        int bytesRead;
        while ((bytesRead = in.read(input)) != -1) {
            byte[] output = cipher.update(input, 0, bytesRead);
            if (output != null) {
                out.write(output);
            }
        }
        byte[] output = cipher.doFinal();
        if (output != null) {
            out.write(output);
        }
        in.close();
        out.flush();
        out.close();
    }
}